package com.qfedu.edu.filter;

import com.qfedu.edu.context.Context;
import com.qfedu.edu.result.R;
import com.qfedu.edu.result.ResponseCode;
import com.qfedu.edu.service.IJwtService;
import com.qfedu.edu.service.ITokenService;
import com.qfedu.edu.utils.HttpResponseUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wangchen
 * @title: AuthFilter
 * @projectName cd-fy-2401-third-project-parent
 * @description: 这个过滤器的主要功能是完成认证
 * @date 2024/9/14  9:08
 */
public class AuthFilter extends BasicAuthenticationFilter {

    private ITokenService tokenService;

    private IJwtService jwtService;

    public AuthFilter(AuthenticationManager authenticationManager, ITokenService tokenService, IJwtService jwtService) {
        super(authenticationManager);
        this.tokenService = tokenService;
        this.jwtService = jwtService;
    }

    /**
     * 在这个方法中就可以完成认证
     *
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        /**
         * 思路：1、校验token的合法性
         *      2、将权限信息和角色信息从内存中取出来放到 Security本身的上下文中去完成鉴权
         *      3、如果还有防止token盗用 那么还要进行时间戳的校验
         */
        //取出这个token
        String token = verifyToken(request, response);
        if (token == null) return;
        setRoleAndPermToContext(token);
        //最后再放行
        chain.doFilter(request, response);
    }

    /**
     * 设置角色和权限到上下文中
     *
     * @param token
     */
    private void setRoleAndPermToContext(String token) {
        //执行到这里说明身份合法
        //从内存中取出这个权限和角色的信息
        List<String> stringList = Context.getRoleAndPermMap().get(token);
        //先从token中找到这个id  username信息
        String userId = jwtService.getUserId(token);
        String username = jwtService.getUsername(token);

        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = null;
        if (null == stringList || stringList.size() == 0) {
            usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userId, username, new ArrayList<>());
        } else {
            List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();
            //接下来需要将上述的权限和角色集合放到这个中
            for (int i = 0; i < stringList.size(); i++) {
                grantedAuthorityList.add(new SimpleGrantedAuthority(stringList.get(i)));
            }
            //然后封装成指定的对象放到Security的上下文中
            usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userId, username, grantedAuthorityList);
        }
        //接下来再将这个数据放到上下文中去
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
    }

    /**
     * token校验
     *
     * @param request
     * @param response
     * @return
     */
    private String verifyToken(HttpServletRequest request, HttpServletResponse response) {
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            R error = R.error(ResponseCode.AUTHENTICATION_EXCEPTION);
            //响应给客户端
            HttpResponseUtils.sendResponse(response, error);
            return null;
        }
        //执行到这里说明参数有参数
        //校验token的合法性
        if (!tokenService.verify(token)) {
            R error = R.error(ResponseCode.AUTHENTICATION_EXCEPTION);
            //响应给客户端
            HttpResponseUtils.sendResponse(response, error);
            return null;
        }
        return token;
    }
}
